
/**
 * @name MASS Heatwave Detects unusually hot days from simulation sample data
 * for January 2-31, 1972 Part of Pacific Northwest Climate Data Analysis
 * Project Proof of concept for MASS parallelization techniques to read netCDF
 * data
 *
 * @version 0.0.309 Pre-Alpha, MASS Heatwave, 9th commit
 * @date 8/18/2013
 * @author Niko Simonson
 * @author Brett Yasutake
 *
 */

// IMPORTS
import MASS.*;                  // MASS Library
import java.io.IOException;     // IO Exceptions
import java.util.*;             // Vectors, etc.
import ucar.ma2.ArrayFloat;     // for netCDF reader
import ucar.ma2.IndexIterator;  // for netCDF reader
import ucar.nc2.NetcdfFile;     // for netCDF reader
import ucar.nc2.Variable;       // for netCDF reader

public class Massheatwave extends Place {

    // CONSTANTS - MASS arguments
    private static final String[] MASSARGS = {"dslab", "dslab-302",
        "machinefile.txt", "12345"};    // connection arguments
    private static final int NPROCESSES = 2;    // number of processes to employ
    private static final int NTHREADS = 2;      // number of threads to employ
    private static final int HANDLE = 0;        // unique Places handle number
    // CONSTANTS - functions
    private static final int init_ = 0;          // initialize Places
    private static final int computeTemp_ = 1;   // compute regional avg temps
    private static final int maxTemp_ = 2;       // exchange regional temps
    private static final int computeMax_ = 3;    // compute daily max temps
    // CONSTANTS - heatwave constants
    private static final int DAYS = 30;          // days of data collection
    private static final int DAYSEGMENTS = 4;    // time slices per day
    // target variable T2: temperature at 2 meters above surface
    private static final String TARGET = "T2";   
    // northwest regional climate data sample
    private static final String FILENAMEBEGIN
            = "/home/brett/netcdf_files/1980_jan/wrfout_d02_1980-01-";
    private static final String FILENAMEEND = ":00:00";  // end of file name
    
    // VARIABLES
    private int sizeX, myX; // time slices in a day
    private int sizeY, myY; // days
    private double avgTemp; // regional average temperature
    private String fileName; // target file

    // PARALLELIZATION FUNCTIONS
    /**
     * *************CONSTRUCTOR
     *
     ***********************************
     * @name Massheatwave
     * @param interval unused Empty constructor required for MASS Place
     * implementation
     * ***********************************************************
     */
    public Massheatwave(Object interval) {
        // A constructor implementation is required for MASS Place
    }

    /**
     * @name callMethod Provides enumerated list of functions to be parallelized
     * in MASS
     *
     * @param funcId - function ID (listed as Constants)
     * @param args - function parameters
     * @return - unused
     */
    public Object callMethod(int funcId, Object args) {
        switch (funcId) {
            case init_:
                return init(args);
            case computeTemp_:
                return computeTemp(args);
            case maxTemp_:
                return maxTemp(args);
            case computeMax_:
                return computeMax(args);
        }
        return null;
    }

    /**
     * @name init Initialization information for MASS Places index[0]: days
     * index[1]: time slices in a day
     *
     * @param args MASS initialization arguments, # of processes, # of threads
     * @return unused
     */
    public Object init(Object args) {
        // record maximum time ranges
        sizeX = DAYSEGMENTS;
        sizeY = DAYS;

        // Each place keeps track of its time position
        myX = index[1];
        myY = index[0];

        return null;
    }

    /**
     * @name computeTemp Open netCDF file and compute average temperature over
     * the entire region
     *
     * @param args unused
     * @return regional average temperature
     */
    public Object computeTemp(Object args) {
        // VARIABLES
        NetcdfFile stubby = null; // target netCDF file

        avgTemp = 0.0;
        try {
            // open target netCDF file
            fileName = getFileName(myX, myY);
            stubby = NetcdfFile.open(fileName, null);	// Open the file

            // Find target variable
            Variable target = stubby.findVariable(TARGET);
            // Get list of file's variables
            List<Variable> stubbyVars = stubby.getVariables();
            Variable curr = stubbyVars.get(stubbyVars.indexOf(target));

            // Read values from variable (temperatures over the region)
            ArrayFloat.D3 d3values = (ArrayFloat.D3) curr.read();
            double totalTemp = 0.0;
            IndexIterator iter = d3values.getIndexIterator();
            while (iter.hasNext()) {
                iter.next();
                totalTemp += (double) iter.getFloatCurrent();
            }
            // calculate regional average temperature
            avgTemp = totalTemp / d3values.getSize();
        } catch (java.io.IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {		// Close out the file no matter what occurs
            if (stubby != null) {
                try {
                    stubby.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
            // Report regional average temperature
            System.err.println("Day " + myY + " Hour " + (myX * 6) + " Temp: "
                    + avgTemp);
            return (Object) avgTemp;
        }
    }

    /**
     * @name getFileName Private helper function to dynamically generate correct
     * file name
     *
     * @param thisX daily time slice
     * @param thisY day
     *
     * @return
     */
    private String getFileName(int thisX, int thisY) {
        // VARIABLES
        String strX, strY;  // file component numbers

        // calculate correct day of the month
        strY = Integer.toString(thisY + 2);

        // calculate correct time of the day
        strX = Integer.toString(thisX * 6);

        // adjust for numbers less than 10
        if (Integer.parseInt(strX) < 10) {
            strX = "0" + strX;
        }
        if (Integer.parseInt(strY) < 10) {
            strY = "0" + strY;
        }

        // return proper file name
        return FILENAMEBEGIN + strY + "_" + strX + FILENAMEEND;
    }

    /**
     * @name computeMax Computes maximum daily temperature
     * @param args unused
     * @return maximum daily temperature
     */
    public Object computeMax(Object args) {
        // VARIABLES
        double max = avgTemp; // maximum temperature

        // Only midnight (00:00:00) of each day will do the work
        if (myX == 0) {
            // look at every other time slice for that day
            for (int i = 0; i < (DAYSEGMENTS - 1); ++i) {
                // if avg temp for that time slice is greater, use it
                if ((double) inMessages[i] > avgTemp) {
                    max = (double) inMessages[i];
                }
            } // next time slice
        } // end if

        // return maximum average temperature for the day
        return max;
    }

    /**
     * @name maxTemp Used for ExchangeAll in preparation of finding maximum
     * temperature
     * @param args unused
     * @return each time slice's avg temp (max temp candidate)
     */
    public double maxTemp(Object args) {
        return avgTemp;
    }

    /**
     * @name main Driver to manage data analysis
     *
     * @param args unused
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // VARIABLES
        long start, end, totalTime; // data provenance for execution time
        // regional temperature calculations
        Object[] results = null;    // holds regional averages
        int count = DAYS * DAYSEGMENTS;     // total number of files
        // dummy object to prevent array over- or underflow
        Object[] fixer = new Object[count];
        double[] mxTemps = new double[DAYS]; // daily max temps
        // candidate max temps for one day
        double[] currRes = new double[DAYSEGMENTS];
        double maxTotal = 0.0; // total of all maximum temperatures 
        int[] dests = new int[DAYS]; // partition of the start of each day
        Object[] mxResults = new Object[DAYS]; // maximum temps for each day
        // interim exchange all method
        Object[] mxResults2 = new Object[DAYS * DAYSEGMENTS];
        double avgMax; // average daily high temperature
        // standard deviation calculations
        double[] deviations; // daily deviations from average high temp
        double totalDev = 0.0; // total of daily deviations    
        double standardDev;  // standard deviation
        // hot day detection results calculations
        double hotDayThreshold; // temperature to count as a "hot day"
        // holds hot days
        HashMap<Integer, Double> hotDays = new HashMap<Integer, Double>();
        Set<Integer> heatwave;  // days that were hot
        Iterator<Integer> finalIter; // iterator over set of daily max temps

        // begin time measurement
        start = new Date().getTime();

        // initialize MASS
        MASS.init(MASSARGS, NPROCESSES, NTHREADS);

        // Create Places
        // Each computing node corresponds to one netCDF file
        Places computeHeatWave = new Places(HANDLE, "Massheatwave", null,
                DAYS, DAYSEGMENTS);

        // Create exchangeAll vector, 
        // corresponding to the three time slices after midnight
        Vector<int[]> segTemps = new Vector<int[]>();
        int[] oneNextSeg = {0, 1};
        int[] twoNextSeg = {0, 2};
        int[] threeNextSeg = {0, 3};
        segTemps.add(oneNextSeg);
        segTemps.add(twoNextSeg);
        segTemps.add(threeNextSeg);

        // Initialize Places
        computeHeatWave.callAll(init_, null);

        // compute regional average temperature at each time slice
        results = computeHeatWave.callAll(computeTemp_, fixer);

        int counter = 0;
        // Calculation of standard deviation
        // get daily max temps
        for (int i = 0; i < DAYS; ++i) {
            dests[i] = i * DAYSEGMENTS;
        }
        try {
            // each file reports its regional average temperature
            computeHeatWave.exchangeAll(HANDLE, maxTemp_, segTemps);

            // dummy object to keep ExchangeAll from array over- or underflow
            Object[] someFix = new Object[3];

            // every day at midnight gets the correct max temp 
            // (e.g. 0, 4, 8, etc.)
            mxResults2 = computeHeatWave.callAll(computeMax_, fixer);

            if (mxResults2 == null) {
                System.err.println("It's null");
            } else {
                for (int i = 0; i < mxResults2.length; ++i) {
                    System.err.println("maxedResult (" + (i + 1) + "): "
                            + mxResults2[i]);
                }
            }

            // initialize mxResults from mxResults2
            for (int j = 0; j < DAYS; ++j) {
                mxResults[j] = mxResults2[j * 4];
                maxTotal += (double) mxResults[j];
            }

            // calculate the average temperature from the maximums
            avgMax = maxTotal / DAYS;

            // calculate the standard deviation
            deviations = new double[mxResults.length];

            for (int i = 0; i < mxResults.length; ++i) {
                deviations[i] = Math.pow((Double) mxResults[i] - avgMax, 2.0);
                totalDev += deviations[i];
            }

            standardDev = Math.sqrt(totalDev / (DAYS - 1));

            hotDayThreshold = avgMax + (1 * standardDev);

            for (int i = 0; i < DAYS; ++i) {
                if ((Double) mxResults[i] > hotDayThreshold) {
                    mxTemps[i] = (Double) mxResults[i];

                    hotDays.put(i, mxTemps[i]);
                }
            }

            // get total execution time
            end = new Date().getTime();
            totalTime = end - start;    //milliseconds

            System.err.println("Final Results (Days Hotter Than "
                    + hotDayThreshold + "):");

            heatwave = hotDays.keySet();
            finalIter = heatwave.iterator();
            while (finalIter.hasNext()) {
                int day = finalIter.next();

                // report final analysis results
                System.err.println("Day: " + day + "  Temp: "
                        + hotDays.get(day));
            }

            // report data provenance (execution time)
            System.err.println("Total execution time (milliseconds): "
                    + totalTime);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // finish MASS
            MASS.finish();
        }
    }
} // end MASS Heatwave
